﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using AsyncCamera.Camera.Structure;
using System.Windows.Threading;

namespace AsyncCamera.Camera.media
{
    /// <summary>
    /// This class represents a media source.
    /// </summary>
    /// <remarks>
    /// A media source is a media elements that has a current frame. It can be a File reader or
    /// an IP camera reader, filters and sinks can be hooked up to it.
    /// </remarks>
    public abstract class MediaSource: MediaElement
    {
        /// <summary>
        /// Reader Writer lock for concurrency control
        /// </summary>
        protected ReaderWriterLockSlim rwlock;
        /// <summary>
        /// Time out of the reader writer lock
        /// </summary>
        protected int timeOut = 1000;

        /// <summary>
        /// Current frame, is thread safe to read and write it
        /// </summary>
        protected Frame frame;
        public Frame Frame
        {
            get
            {
                rwlock.EnterReadLock();
                try
                {
                    return frame;
                }
                finally
                {
                    rwlock.ExitReadLock();
                }
            }
            set
            {
                if (rwlock.TryEnterWriteLock(timeOut))
                {
                    try
                    {
                        if (frame == null || frame.Id < value.Id || frame.Id == Double.MaxValue)
                        {
                            this.frame = value;
                        }
                    }
                    finally
                    {
                        rwlock.ExitWriteLock();
                    }
                }
            }
        }

        /// <summary>
        /// Basic constructor
        /// </summary>
        /// <see cref="MediaElement"/>
        public MediaSource():base()
        {
            Initialize();
        }

        /// <summary>
        /// Basic initialization, creates a new lock and sets a default time out of 1 sec
        /// </summary>
        protected override void Initialize()
        {
            rwlock = new ReaderWriterLockSlim();
            timeOut = 1000;
        }

        /// <summary>
        /// Starts the element and sets the current state to <c>MediaState.Play</c>
        /// </summary>
        /// <remarks>
        /// This method starts the internal metronome with the given fps. If no metronome
        /// is find then a new metronome is created
        /// </remarks>
        public override void Start()
        {
            switch (currentState)
            {
                case MediaState.Stop:
                    try
                    {
                        metronome = new DispatcherTimer();
                        metronome.Interval = new TimeSpan((long)(10000000 / fps));
                        metronome.Tick += new EventHandler(Tick);
                        metronome.Start();
                        currentState = MediaState.Play;
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                    break;
                case MediaState.Pause:
                    metronome.Stop();
                    currentState = MediaState.Play;
                    break;
            }
        }

        ///  <summary>
        /// Stops the element and sets the current state to <c>MediaState.Stop</c>
        /// </summary>
        /// <remarks>
        /// This method stops the internal metronome and destroys it.
        /// </remarks>
        public override void Stop()
        {
            switch (currentState)
            {
                case MediaState.Play:
                    metronome.Stop();
                    metronome = null;
                    currentState = MediaState.Stop;
                    break;
                case MediaState.Pause:
                    metronome = null;
                    currentState = MediaState.Stop;
                    break;
            }
        }

        ///  <summary>
        /// Pauses the element and sets the current state to <c>MediaState.Pause</c>
        /// </summary>
        /// <remarks>
        /// This method stops the internal metronome.
        /// </remarks>
        public override void Pause()
        {
            switch (currentState)
            {
                case AsyncCamera.Camera.Structure.MediaState.Play:
                    metronome.Stop();
                    currentState = MediaState.Pause;
                    break;
            }
        }
    }
}
